package com.ruowei.modules.wxpay.service;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ruowei.common.error.exception.CommonException;
import com.ruowei.config.ApplicationProperties;
import com.ruowei.modules.foodOrder.domain.Order;
import com.ruowei.modules.foodOrder.domain.RefundApplication;
import com.ruowei.modules.foodOrder.domain.enumeration.FlowType;
import com.ruowei.modules.foodOrder.domain.enumeration.OrderStatus;
import com.ruowei.modules.foodOrder.repository.OrderRepository;
import com.ruowei.modules.foodOrder.repository.RefundApplicationRepository;
import com.ruowei.modules.foodOrder.service.LifeCycleService;
import com.ruowei.modules.foodOrder.service.MerchantService;
import com.ruowei.modules.foodOrder.service.PaymentFlowService;
import com.ruowei.modules.foodOrder.service.ShoppingCartService;
import com.ruowei.modules.member.domain.Member;
import com.ruowei.modules.sys.utils.*;
import com.ruowei.modules.webSocket.domain.enumeration.MessageType;
import com.ruowei.modules.webSocket.domain.enumeration.ReceiverType;
import com.ruowei.modules.webSocket.domain.enumeration.SenderType;
import com.ruowei.modules.webSocket.service.MessagePushService;
import com.ruowei.modules.wxpay.domain.enumeration.PayReturnType;
import com.ruowei.modules.wxpay.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;

@Service
@Slf4j
public class WeChatPayService {
    private final ApplicationProperties applicationProperties;

    private final OrderRepository orderRepository;
    private final MessagePushService messagePushService;
    private final RedisUtil redisUtil;
    private final PayReturnService returnService;
    private final LifeCycleService lifeCycleService;
    private final ShoppingCartService shoppingCartService;
    private final RefundApplicationRepository refundApplicationRepository;
    private final MerchantService merchantService;
    private final PaymentFlowService paymentFlowService;
    private final Gson gson = new GsonBuilder().create();

    public WeChatPayService(ApplicationProperties applicationProperties, OrderRepository orderRepository, MessagePushService messagePushService, RedisUtil redisUtil, PayReturnService returnService, LifeCycleService lifeCycleService, ShoppingCartService shoppingCartService, RefundApplicationRepository refundApplicationRepository, MerchantService merchantService, PaymentFlowService paymentFlowService) {
        this.applicationProperties = applicationProperties;
        this.orderRepository = orderRepository;
        this.messagePushService = messagePushService;
        this.redisUtil = redisUtil;
        this.returnService = returnService;
        this.lifeCycleService = lifeCycleService;
        this.shoppingCartService = shoppingCartService;
        this.refundApplicationRepository = refundApplicationRepository;
        this.merchantService = merchantService;
        this.paymentFlowService = paymentFlowService;
    }

    /**
     * @param request
     * @param body     格式 商家名称-商品名称
     * @param totalFee 金额，单位是分
     * @apiNote 统一下单
     * @author 董兴
     * @date 2020-11-06
     */
    public Map<String, String> unifiedOrder(HttpServletRequest request, String body, String totalFee, String orderCode) {
        Map<String, String> result = new HashMap<>();
        //获取小程序登录人信息
        Member member = AppletTokenUtils.getOpenIdByToken(request);
        //如果能找到订单，则是重新支付，否则是第一次下单并支付
        Order order = orderRepository.findByOrderCode(orderCode).orElse(new Order());
        try {
            Map<String, String> signMap = new HashMap<>();
            signMap.put("appid", applicationProperties.getAppId());
            signMap.put("nonce_str", WXPayUtil.generateNonceStr());
            signMap.put("body", body);
            signMap.put("out_trade_no", orderCode);
            signMap.put("spbill_create_ip", StringUtils.getIpAddress(request));
            signMap.put("notify_url", applicationProperties.getNotifyUrl());
            signMap.put("trade_type", "JSAPI");
            signMap.put("openid", member.getOpenId());

            // 生产环境
            signMap.put("total_fee", totalFee);
            signMap.put("mch_id", applicationProperties.getMchId());
            //生成请求报文
            String signMapXml = WXPayUtil.generateSignedXml(signMap, applicationProperties.getMchKey());
            WXPayConfig wxPayConfig = new WXPayConfig(
                applicationProperties.getAppId(),
                applicationProperties.getMchId(),
                applicationProperties.getMchKey());
            WXPay wxPay = new WXPay(wxPayConfig,
                null,
                true,
                false);

            // 返回的Map 如果失败直接返回 如果成功则是已经验证签名
            Map<String, String> resMap = wxPay.unifiedOrder(WXPayUtil.xmlToMap(signMapXml));

            //微信返回报文转成json
            String jsonData = gson.toJson(resMap);

            //打印微信返回数据
            log.info("【下单返回数据】{}", jsonData);

            //保存微信返回数据到数据库
            new Thread(() -> returnService.saveReturnData(orderCode, null, jsonData, PayReturnType.CREATE_ORDER)).start();

            if (resMap.get("return_code").equals("SUCCESS") && resMap.get("result_code").equals("SUCCESS")) {
                // 返回支付参数（5个参数和sign）
                //清空购物车
                shoppingCartService.clearShoppingCart(order.getMerchantCode(), request);
                result.put("appId", applicationProperties.getAppId());
                result.put("timeStamp", String.valueOf(WXPayUtil.getCurrentTimestamp()));
                result.put("nonceStr", WXPayUtil.generateNonceStr());
                result.put("package", "prepay_id=" + resMap.get("prepay_id"));
                result.put("signType", "HMAC-SHA256");
                String resultXml = WXPayUtil.generateSignedXml(result, applicationProperties.getMchKey(), WXPayConstants.SignType.HMACSHA256);
                result.clear();
                result = WXPayUtil.xmlToMap(resultXml);
                result.put("code", "1");
                result.put("orderCode", orderCode);
                return result;
            } else {
                log.error(WXPayUtil.mapToXml(resMap));
                throw new CommonException("系统错误，生成订单失败，请重新操作。");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException("系统错误，生成订单失败，请重新操作。");
        }
    }

    /**
     * 微信支付订单查询
     * trade_state
     * SUCCESS—支付成功
     * REFUND—转入退款
     * NOTPAY—未支付
     * CLOSED—已关闭
     * REVOKED—已撤销（刷卡支付）
     * USERPAYING--用户支付中
     * PAYERROR--支付失败(其他原因，如银行返回失败)
     */
    public Map<String, String> queryOrder(String orderCode) throws Exception {
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", applicationProperties.getAppId());
        paramMap.put("mch_id", applicationProperties.getMchId());
        paramMap.put("out_trade_no", orderCode);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("sign_type", "MD5");
        String paramMapXml = WXPayUtil.generateSignedXml(paramMap, applicationProperties.getMchKey());
        WXPayConfig wxPayConfig = new WXPayConfig(applicationProperties.getAppId(), applicationProperties.getMchId(), applicationProperties.getMchKey());
        WXPay wxPay = new WXPay(wxPayConfig, null, true, false);
        //微信返回数据
        Map<String, String> resMap = wxPay.orderQuery(WXPayUtil.xmlToMap(paramMapXml));

        String jsonData = gson.toJson(resMap);
        log.info("【微信支付订单查询返回数据】{}", jsonData);
        return resMap;
    }

    /**
     * @param orderCode
     * @apiNote 退款订单查询
     * @author 董兴
     */
    public Map<String, String> refundQuery(String orderCode) throws Exception {
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", applicationProperties.getAppId());
        paramMap.put("mch_id", applicationProperties.getMchId());
        paramMap.put("out_trade_no", orderCode);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("sign_type", "MD5");
        String paramMapXml = WXPayUtil.generateSignedXml(paramMap, applicationProperties.getMchKey());
        WXPayConfig wxPayConfig = new WXPayConfig(applicationProperties.getAppId(), applicationProperties.getMchId(), applicationProperties.getMchKey());
        WXPay wxPay = new WXPay(wxPayConfig, null, true, false);
        Map<String, String> resMap = wxPay.refundQuery(WXPayUtil.xmlToMap(paramMapXml));

        String jsonData = gson.toJson(resMap);
        log.info("【微信支付退款查询返回数据】{}", jsonData);
        return resMap;
    }

    /**
     * @param amount
     * @param orderCode
     * @apiNote 退款
     * @author 董兴
     * @date 2020-12-01
     */
    public Map<String, String> refund(BigDecimal amount, String orderCode) throws Exception {
        String fee = String.valueOf((int) (amount.doubleValue() * 100));
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", applicationProperties.getAppId());
        paramMap.put("mch_id", applicationProperties.getMchId());
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        //sign_type 默认MD5
//        paramMap.put("sign_type", "HMAC-SHA256");
        paramMap.put("out_trade_no", orderCode);
        paramMap.put("out_refund_no", WXPayUtil.createOrderCode());
        paramMap.put("total_fee", fee);
        paramMap.put("refund_fee", fee);
        paramMap.put("notify_url", applicationProperties.getRefundNotify());
        String paramMapXml = WXPayUtil.generateSignedXml(paramMap, applicationProperties.getMchKey());
        WXPayConfig wxPayConfig = new WXPayConfig(applicationProperties.getAppId(), applicationProperties.getMchId(), applicationProperties.getMchKey(), applicationProperties.getCertPath());
        WXPay wxPay = new WXPay(wxPayConfig, null, true, false);
        Map<String, String> resMap = wxPay.refund(WXPayUtil.xmlToMap(paramMapXml));

        String jsonData = gson.toJson(resMap);
        log.info("【申请退款返回数据】{}", jsonData);
        new Thread(() -> returnService.saveReturnData(orderCode, paramMap.get("out_refund_no"), jsonData, PayReturnType.REFUND)).start();

        return resMap;
    }

    /**
     * @param request
     * @param response
     * @apiNote 下单回调
     * @author 董兴
     * @date 2020-11-09
     */
    public synchronized void orderNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> reqMap = WXPayUtil.getNotifyParameter(request);
        String orderCode = reqMap.get("out_trade_no");

        String jsonData = gson.toJson(reqMap);
        new Thread(() -> returnService.saveReturnData(orderCode, null, jsonData, PayReturnType.CREATE_ORDER_NOTIFY)).start();
        log.info("【下单回调返回数据】{}", jsonData);
        sendResponse(response);

//        if (reqMap.get("return_code") != null && reqMap.get("return_code").equals("SUCCESS")) {
//            // 支付回调处理
//            String signStr = WXPayUtil.generateSignature(reqMap, applicationProperties.getMchKey(), WXPayConstants.SignType.HMACSHA256);
//            // 验证支付结果通知签名
//            if (signStr.equals(reqMap.get("sign"))) {
//                // 如果交易失败，则通知微信端已受理，但此处不做任何处理，交由订单查询处理
//                if (!reqMap.get("result_code").equals("SUCCESS")) {
//                    sendResponse(response);
//                    return;
//                }
//                //交易成功，更新订单状态
//                Optional<Order> orderOptional = orderRepository.findByOrderCode(orderCode);
//                if (orderOptional.isPresent()) {
//                    Order order = orderOptional.get();
//                    if (order.getStatus().equals(OrderStatus.TO_BE_PAID) || order.getPaymentTime() == null) {
//                        long time = System.currentTimeMillis();
//                        if (!redisUtil.lock(orderCode, time)) {
//                            order.setStatus(OrderStatus.TO_BE_RECEIVED);
//                            order.setPaymentTime(Instant.now());
//                            order.setUpdateTime(Instant.now());
//                            orderRepository.saveAndFlush(order);
//                            redisUtil.unlock(orderCode, time);
//                        }
//
//                        //保存流水
//                        paymentFlowService.savePaymentFlow(order.getOrderCode(),
//                            null,
//                            order.getMemberCode(),
//                            FlowType.ORDER,
//                            order.getMerchantCode(),
//                            merchantService.getMerchantNameBy(order.getMerchantCode()),
//                            order.getPayAmount());
//
//                        lifeCycleService.saveLifeCycle(order.getOrderCode(), null, "支付成功", "支付成功");
//
//                        //给商家发消息
//                        Map<String, String> customMessage = new HashMap<>();
//                        customMessage.put("id", String.valueOf(order.getId()));
//                        customMessage.put("orderCode", order.getOrderCode());
//                        messagePushService.pushMessage(SenderType.SYSTEM,
//                            null,
//                            order.getMerchantCode(),
//                            MessageType.FOOD_ORDER,
//                            true,
//                            "新的订单",
//                            "您有新的订单",
//                            customMessage,
//                            ReceiverType.MERCHANT_APP);
//                    }
//                }
//            }
//        }
    }

    /**
     * @param response
     * @apiNote 回应微信
     * @author 董兴
     */
    private void sendResponse(HttpServletResponse response) throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("return_code", "SUCCESS");
        map.put("return_msg", "OK");
        String responseXml = WXPayUtil.mapToXml(map);
        response.setContentType("text/xml");
        ServletOutputStream sos = response.getOutputStream();
        sos.write(responseXml.getBytes(StandardCharsets.UTF_8));
        sos.flush();
        sos.close();
    }

    /**
     * @param request
     * @param response
     * @apiNote 退款回调
     * @author 董兴
     * @date 2020-11-09
     */
    public void refundNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

        Map<String, String> reqMap = WXPayUtil.getNotifyParameter(request);
        String jsonData = gson.toJson(reqMap);
        log.info("【退款回调返回数据】{}", jsonData);

        // 验证支付结果通知签名
        if (reqMap.get("return_code").equals("SUCCESS")) {
            //退款回调处理
            // 解密
            String data = AESUtil.decryptData(reqMap.get("req_info"), applicationProperties.getMchKey());
            Map<String, String> dMap = WXPayUtil.xmlToMap(data);
            String orderCode = dMap.get("out_trade_no");
            String orderReturnCode = dMap.get("out_refund_no");
//            OrderStatus status;
//            String title = "";
//            String content = "";
//            boolean push = false;
//            if (dMap.get("refund_status").equals("SUCCESS")) {
//                //退款成功处理
//                status = OrderStatus.REFUNDED;
//                title = "退款成功";
//                content = "您的退款已经返回到您的账户，请查看";
//                push = true;
//                //保存流水
//                Optional<Order> orderOptional = orderRepository.findByOrderCode(orderCode);
//
//                if (orderOptional.isPresent()) {
//                    Order order = orderOptional.get();
//                    paymentFlowService.savePaymentFlow(order.getOrderCode(),
//                        orderReturnCode,
//                        order.getMemberCode(),
//                        FlowType.REFUND,
//                        order.getMerchantCode(),
//                        merchantService.getMerchantNameBy(order.getMerchantCode()),
//                        order.getPayAmount());
//                }
//                lifeCycleService.saveLifeCycle(orderCode, orderReturnCode, "已退款", "您的退款已经返回到您的账户，请查看");
//            } else if (dMap.get("refund_status").equals("CHANGE")) {
//                //退款失败处理
//                status = OrderStatus.REFUND_FAILED;
//                title = "退款失败";
//                content = "您的退款处理异常，请联系平台管理员";
//                push = true;
//                lifeCycleService.saveLifeCycle(orderCode, orderReturnCode, "退款失败", "您的退款处理异常，请联系平台管理员");
//            } else {
//                //退款关闭处理
//                status = OrderStatus.REFUND_CLOSED;
//                lifeCycleService.saveLifeCycle(orderCode, orderReturnCode, "退款关闭", "您的退款申请已关闭");
//            }

            new Thread(() -> returnService.saveReturnData(orderCode, orderReturnCode, jsonData, PayReturnType.REFUND_NOTIFY)).start();
//            Optional<Order> orderOptional = orderRepository.findByOrderCode(orderCode);
//            if (orderOptional.isPresent()) {
//                Order order = orderOptional.get();

            //退款结果推送
//                if (push) {
//                    Map<String, String> customMessage = new HashMap<>();
//                    customMessage.put("orderCode", order.getOrderCode());
//                    customMessage.put("status", order.getStatus().getMessage());
//                    messagePushService.pushMessage(SenderType.SYSTEM,
//                        null,
//                        order.getMemberCode(),
//                        MessageType.FOOD_ORDER,
//                        true,
//                        title,
//                        content,
//                        customMessage,
//                        ReceiverType.APPLET
//                    );
//                }
//                long time = System.currentTimeMillis();
//
//                List<OrderStatus> statuses = new ArrayList<>(Arrays.asList(OrderStatus.CANCELLING, OrderStatus.REFUND_AGREE, OrderStatus.MERCHANT_REFUSE_TO_RECEIVE));
//                if (statuses.contains(order.getStatus())) {
//                    //更新订单信息
//                    if (!redisUtil.lock(orderCode, time)) {
//                        return;
//                    }
//                    order.setStatus(status);
//                    order.setUpdateTime(Instant.now());
//                    orderRepository.saveAndFlush(order);
//                    redisUtil.unlock(orderCode, time);
//                }

//                Optional<RefundApplication> applicationOptional = refundApplicationRepository.findFirstByOrderCodeOrderByTimeDesc(order.getOrderCode());
//                if (applicationOptional.isPresent()) {
//                    //如果有对应的退款申请，更新申请信息
//                    RefundApplication application = applicationOptional.get();
//                    application.setStatus(status);
//                    application.setDescription(content);
//                    refundApplicationRepository.saveAndFlush(application);
//                }
//            }
        }
        this.sendResponse(response);

    }

}
